<?xml version="1.0" encoding="utf-8" standalone="no"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
        "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <link href="../Styles/sure3.css" rel="stylesheet" type="text/css"/>
    <link href="../Styles/base1.css" rel="stylesheet" type="text/css"/>
</head>

<body>
<div class="markdown-body entry-content" itemprop="text">
    <h1><a href="#class-的继承"  class="anchor" id="user-content-class-的继承">

    </a>Class 的继承
    </h1>
    <h2><a href="#简介"  class="anchor" id="user-content-简介">

    </a>简介
    </h2>
    <p>Class 可以通过<code>extends</code>关键字实现继承，这比 ES5 的通过修改原型链实现继承，要清晰和方便很多。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">Point</span> {
}

<span class="pl-k">class</span> <span class="pl-en">ColorPoint</span> <span class="pl-k">extends</span> <span
                class="pl-e">Point</span> {
}</pre>
    </div>
    <p>上面代码定义了一个<code>ColorPoint</code>类，该类通过<code>extends</code>关键字，继承了<code>Point</code>类的所有属性和方法。但是由于没有部署任何代码，所以这两个类完全一样，等于复制了一个<code>Point</code>类。下面，我们在<code>ColorPoint</code>内部加上代码。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">ColorPoint</span> <span class="pl-k">extends</span> <span class="pl-e">Point</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">x</span>, <span class="pl-smi">y</span>, <span
                class="pl-smi">color</span>) {
    <span class="pl-c1">super</span>(x, y); <span class="pl-c"><span
                class="pl-c">//</span> 调用父类的constructor(x, y)</span>
    <span class="pl-c1">this</span>.<span class="pl-c1">color</span> <span class="pl-k">=</span> color;
  }

  <span class="pl-en">toString</span>() {
    <span class="pl-k">return</span> <span class="pl-c1">this</span>.<span class="pl-c1">color</span> <span
                class="pl-k">+</span> <span class="pl-s"><span class="pl-pds">'</span> <span
                class="pl-pds">'</span></span> <span class="pl-k">+</span> <span class="pl-c1">super</span>.<span
                class="pl-c1">toString</span>(); <span class="pl-c"><span class="pl-c">//</span> 调用父类的toString()</span>
  }
}</pre>
    </div>
    <p>上面代码中，<code>constructor</code>方法和<code>toString</code>方法之中，都出现了<code>super</code>关键字，它在这里表示父类的构造函数，用来新建父类的<code>this</code>对象。
    </p>
    <p>子类必须在<code>constructor</code>方法中调用<code>super</code>方法，否则新建实例时会报错。这是因为子类没有自己的<code>this</code>对象，而是继承父类的<code>this</code>对象，然后对其进行加工。如果不调用<code>super</code>方法，子类就得不到<code>this</code>对象。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">Point</span> { <span class="pl-c"><span class="pl-c">/*</span> ... <span
            class="pl-c">*/</span></span> }

<span class="pl-k">class</span> <span class="pl-en">ColorPoint</span> <span class="pl-k">extends</span> <span
                class="pl-e">Point</span> {
  <span class="pl-en">constructor</span>() {
  }
}

<span class="pl-k">let</span> cp <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">ColorPoint</span>(); <span
                class="pl-c"><span class="pl-c">//</span> ReferenceError</span></pre>
    </div>
    <p>上面代码中，<code>ColorPoint</code>继承了父类<code>Point</code>，但是它的构造函数没有调用<code>super</code>方法，导致新建实例时报错。</p>
    <p>ES5 的继承，实质是先创造子类的实例对象<code>this</code>，然后再将父类的方法添加到<code>this</code>上面（<code>Parent.apply(this)</code>）。ES6
        的继承机制完全不同，实质是先创造父类的实例对象<code>this</code>（所以必须先调用<code>super</code>方法），然后再用子类的构造函数修改<code>this</code>。</p>
    <p>如果子类没有定义<code>constructor</code>方法，这个方法会被默认添加，代码如下。也就是说，不管有没有显式定义，任何一个子类都有<code>constructor</code>方法。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">ColorPoint</span> <span class="pl-k">extends</span> <span class="pl-e">Point</span> {
}

<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-k">class</span> <span class="pl-en">ColorPoint</span> <span class="pl-k">extends</span> <span
                class="pl-e">Point</span> {
  <span class="pl-en">constructor</span>(<span class="pl-k">...</span><span class="pl-v">args</span>) {
    <span class="pl-c1">super</span>(<span class="pl-k">...</span>args);
  }
}</pre>
    </div>
    <p>另一个需要注意的地方是，在子类的构造函数中，只有调用<code>super</code>之后，才可以使用<code>this</code>关键字，否则会报错。这是因为子类实例的构建，是基于对父类实例加工，只有<code>super</code>方法才能返回父类实例。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">Point</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">x</span>, <span class="pl-smi">y</span>) {
    <span class="pl-c1">this</span>.<span class="pl-c1">x</span> <span class="pl-k">=</span> x;
    <span class="pl-c1">this</span>.<span class="pl-c1">y</span> <span class="pl-k">=</span> y;
  }
}

<span class="pl-k">class</span> <span class="pl-en">ColorPoint</span> <span class="pl-k">extends</span> <span
                class="pl-e">Point</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">x</span>, <span class="pl-smi">y</span>, <span
                class="pl-smi">color</span>) {
    <span class="pl-c1">this</span>.<span class="pl-c1">color</span> <span class="pl-k">=</span> color; <span
                class="pl-c"><span class="pl-c">//</span> ReferenceError</span>
    <span class="pl-c1">super</span>(x, y);
    <span class="pl-c1">this</span>.<span class="pl-c1">color</span> <span class="pl-k">=</span> color; <span
                class="pl-c"><span class="pl-c">//</span> 正确</span>
  }
}</pre>
    </div>
    <p>上面代码中，子类的<code>constructor</code>方法没有调用<code>super</code>之前，就使用<code>this</code>关键字，结果报错，而放在<code>super</code>方法之后就是正确的。
    </p>
    <p>下面是生成子类实例的代码。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">let</span> cp <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">ColorPoint</span>(<span class="pl-c1">25</span>, <span
            class="pl-c1">8</span>, <span class="pl-s"><span class="pl-pds">'</span>green<span
            class="pl-pds">'</span></span>);

cp <span class="pl-k">instanceof</span> ColorPoint <span class="pl-c"><span class="pl-c">//</span> true</span>
cp <span class="pl-k">instanceof</span> <span class="pl-c1">Point</span> <span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，实例对象<code>cp</code>同时是<code>ColorPoint</code>和<code>Point</code>两个类的实例，这与 ES5 的行为完全一致。</p>
    <p>最后，父类的静态方法，也会被子类继承。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
  <span class="pl-k">static</span> <span class="pl-en">hello</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>hello world<span
                class="pl-pds">'</span></span>);
  }
}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
}

<span class="pl-c1">B</span>.<span class="pl-en">hello</span>()  <span class="pl-c"><span class="pl-c">//</span> hello world</span></pre>
    </div>
    <p>上面代码中，<code>hello()</code>是<code>A</code>类的静态方法，<code>B</code>继承<code>A</code>，也继承了<code>A</code>的静态方法。</p>
    <h2><a href="#objectgetprototypeof"  class="anchor" id="user-content-objectgetprototypeof">

    </a>Object.getPrototypeOf()
    </h2>
    <p><code>Object.getPrototypeOf</code>方法可以用来从子类上获取父类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span
            class="pl-en">getPrototypeOf</span>(ColorPoint) <span class="pl-k">===</span> <span
            class="pl-c1">Point</span>
<span class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>因此，可以使用这个方法判断，一个类是否继承了另一个类。</p>
    <h2><a href="#super-关键字"  class="anchor" id="user-content-super-关键字">

    </a>super 关键字
    </h2>
    <p><code>super</code>这个关键字，既可以当作函数使用，也可以当作对象使用。在这两种情况下，它的用法完全不同。</p>
    <p>第一种情况，<code>super</code>作为函数调用时，代表父类的构造函数。ES6 要求，子类的构造函数必须执行一次<code>super</code>函数。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
  }
}</pre>
    </div>
    <p>上面代码中，子类<code>B</code>的构造函数之中的<code>super()</code>，代表调用父类的构造函数。这是必须的，否则 JavaScript 引擎会报错。</p>
    <p>注意，<code>super</code>虽然代表了父类<code>A</code>的构造函数，但是返回的是子类<code>B</code>的实例，即<code>super</code>内部的<code>this</code>指的是<code>B</code>，因此<code>super()</code>在这里相当于<code>A.prototype.constructor.call(this)</code>。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-k">new</span>.<span class="pl-c1">target</span>.<span
                class="pl-c1">name</span>);
  }
}
<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
  }
}
<span class="pl-k">new</span> <span class="pl-en">A</span>() <span class="pl-c"><span class="pl-c">//</span> A</span>
<span class="pl-k">new</span> <span class="pl-en">B</span>() <span class="pl-c"><span
                class="pl-c">//</span> B</span></pre>
    </div>
    <p>
        上面代码中，<code>new.target</code>指向当前正在执行的函数。可以看到，在<code>super()</code>执行时，它指向的是子类<code>B</code>的构造函数，而不是父类<code>A</code>的构造函数。也就是说，<code>super()</code>内部的<code>this</code>指向的是<code>B</code>。
    </p>
    <p>作为函数时，<code>super()</code>只能用在子类的构造函数之中，用在其他地方就会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">m</span>() {
    <span class="pl-c1">super</span>(); <span class="pl-c"><span class="pl-c">//</span> 报错</span>
  }
}</pre>
    </div>
    <p>上面代码中，<code>super()</code>用在<code>B</code>类的<code>m</code>方法之中，就会造成句法错误。</p>
    <p>第二种情况，<code>super</code>作为对象时，在普通方法中，指向父类的原型对象；在静态方法中，指向父类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
  <span class="pl-en">p</span>() {
    <span class="pl-k">return</span> <span class="pl-c1">2</span>;
  }
}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">super</span>.<span
                class="pl-en">p</span>()); <span class="pl-c"><span class="pl-c">//</span> 2</span>
  }
}

<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">B</span>();</pre>
    </div>
    <p>
        上面代码中，子类<code>B</code>当中的<code>super.p()</code>，就是将<code>super</code>当作一个对象使用。这时，<code>super</code>在普通方法之中，指向<code>A.prototype</code>，所以<code>super.p()</code>就相当于<code>A.prototype.p()</code>。
    </p>
    <p>这里需要注意，由于<code>super</code>指向父类的原型对象，所以定义在父类实例上的方法或属性，是无法通过<code>super</code>调用的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">this</span>.<span class="pl-smi">p</span> <span class="pl-k">=</span> <span
                class="pl-c1">2</span>;
  }
}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-k">get</span> <span class="pl-en">m</span>() {
    <span class="pl-k">return</span> <span class="pl-c1">super</span>.<span class="pl-smi">p</span>;
  }
}

<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">B</span>();
<span class="pl-smi">b</span>.<span class="pl-smi">m</span> <span class="pl-c"><span
                class="pl-c">//</span> undefined</span></pre>
    </div>
    <p>上面代码中，<code>p</code>是父类<code>A</code>实例的属性，<code>super.p</code>就引用不到它。</p>
    <p>如果属性定义在父类的原型对象上，<code>super</code>就可以取到。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {}
<span class="pl-c1">A</span>.<span class="pl-c1">prototype</span>.<span class="pl-c1">x</span> <span
                class="pl-k">=</span> <span class="pl-c1">2</span>;

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">super</span>.<span
                class="pl-c1">x</span>) <span class="pl-c"><span class="pl-c">//</span> 2</span>
  }
}

<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">B</span>();</pre>
    </div>
    <p>上面代码中，属性<code>x</code>是定义在<code>A.prototype</code>上面的，所以<code>super.x</code>可以取到它的值。</p>
    <p>ES6 规定，通过<code>super</code>调用父类的方法时，方法内部的<code>this</code>指向子类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">this</span>.<span class="pl-c1">x</span> <span class="pl-k">=</span> <span
                class="pl-c1">1</span>;
  }
  <span class="pl-en">print</span>() {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">this</span>.<span
                class="pl-c1">x</span>);
  }
}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
    <span class="pl-c1">this</span>.<span class="pl-c1">x</span> <span class="pl-k">=</span> <span
                class="pl-c1">2</span>;
  }
  <span class="pl-en">m</span>() {
    <span class="pl-c1">super</span>.<span class="pl-c1">print</span>();
  }
}

<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">B</span>();
<span class="pl-smi">b</span>.<span class="pl-en">m</span>() <span class="pl-c"><span
                class="pl-c">//</span> 2</span></pre>
    </div>
    <p>
        上面代码中，<code>super.print()</code>虽然调用的是<code>A.prototype.print()</code>，但是<code>A.prototype.print()</code>内部的<code>this</code>指向子类<code>B</code>，导致输出的是<code>2</code>，而不是<code>1</code>。也就是说，实际上执行的是<code>super.print.call(this)</code>。
    </p>
    <p>由于<code>this</code>指向子类，所以如果通过<code>super</code>对某个属性赋值，这时<code>super</code>就是<code>this</code>，赋值的属性会变成子类实例的属性。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">this</span>.<span class="pl-c1">x</span> <span class="pl-k">=</span> <span
                class="pl-c1">1</span>;
  }
}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
    <span class="pl-c1">this</span>.<span class="pl-c1">x</span> <span class="pl-k">=</span> <span
                class="pl-c1">2</span>;
    <span class="pl-c1">super</span>.<span class="pl-c1">x</span> <span class="pl-k">=</span> <span
                class="pl-c1">3</span>;
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">super</span>.<span
                class="pl-c1">x</span>); <span class="pl-c"><span class="pl-c">//</span> undefined</span>
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">this</span>.<span
                class="pl-c1">x</span>); <span class="pl-c"><span class="pl-c">//</span> 3</span>
  }
}

<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">B</span>();</pre>
    </div>
    <p>上面代码中，<code>super.x</code>赋值为<code>3</code>，这时等同于对<code>this.x</code>赋值为<code>3</code>。而当读取<code>super.x</code>的时候，读的是<code>A.prototype.x</code>，所以返回<code>undefined</code>。
    </p>
    <p>如果<code>super</code>作为对象，用在静态方法之中，这时<code>super</code>将指向父类，而不是父类的原型对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">Parent</span> {
  <span class="pl-k">static</span> <span class="pl-en">myMethod</span>(<span class="pl-smi">msg</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>static<span
                class="pl-pds">'</span></span>, msg);
  }

  <span class="pl-en">myMethod</span>(<span class="pl-smi">msg</span>) {
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span class="pl-pds">'</span>instance<span
                class="pl-pds">'</span></span>, msg);
  }
}

<span class="pl-k">class</span> <span class="pl-en">Child</span> <span class="pl-k">extends</span> <span class="pl-e">Parent</span> {
  <span class="pl-k">static</span> <span class="pl-en">myMethod</span>(<span class="pl-smi">msg</span>) {
    <span class="pl-c1">super</span>.<span class="pl-en">myMethod</span>(msg);
  }

  <span class="pl-en">myMethod</span>(<span class="pl-smi">msg</span>) {
    <span class="pl-c1">super</span>.<span class="pl-en">myMethod</span>(msg);
  }
}

<span class="pl-smi">Child</span>.<span class="pl-en">myMethod</span>(<span class="pl-c1">1</span>); <span class="pl-c"><span
                class="pl-c">//</span> static 1</span>

<span class="pl-k">var</span> child <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">Child</span>();
<span class="pl-smi">child</span>.<span class="pl-en">myMethod</span>(<span class="pl-c1">2</span>); <span class="pl-c"><span
                class="pl-c">//</span> instance 2</span></pre>
    </div>
    <p>上面代码中，<code>super</code>在静态方法之中指向父类，在普通方法之中指向父类的原型对象。</p>
    <p>注意，使用<code>super</code>的时候，必须显式指定是作为函数、还是作为对象使用，否则会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">super</span>); <span
                class="pl-c"><span class="pl-c">//</span> 报错</span>
  }
}</pre>
    </div>
    <p>上面代码中，<code>console.log(super)</code>当中的<code>super</code>，无法看出是作为函数使用，还是作为对象使用，所以 JavaScript
        引擎解析代码的时候就会报错。这时，如果能清晰地表明<code>super</code>的数据类型，就不会报错。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
    <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-c1">super</span>.<span
                class="pl-c1">valueOf</span>() <span class="pl-k">instanceof</span> <span class="pl-c1">B</span>); <span
                class="pl-c"><span class="pl-c">//</span> true</span>
  }
}

<span class="pl-k">let</span> b <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">B</span>();</pre>
    </div>
    <p>
        上面代码中，<code>super.valueOf()</code>表明<code>super</code>是一个对象，因此就不会报错。同时，由于<code>super</code>使得<code>this</code>指向<code>B</code>，所以<code>super.valueOf()</code>返回的是一个<code>B</code>的实例。
    </p>
    <p>最后，由于对象总是继承其他对象的，所以可以在任意一个对象中，使用<code>super</code>关键字。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> obj <span class="pl-k">=</span> {
  <span class="pl-en">toString</span>() {
    <span class="pl-k">return</span> <span class="pl-s"><span class="pl-pds">"</span>MyObject: <span
                class="pl-pds">"</span></span> <span class="pl-k">+</span> <span class="pl-c1">super</span>.<span
                class="pl-c1">toString</span>();
  }
};

<span class="pl-smi">obj</span>.<span class="pl-c1">toString</span>(); <span class="pl-c"><span class="pl-c">//</span> MyObject: [object Object]</span></pre>
    </div>
    <h2><a href="#类的-prototype-属性和__proto__属性"  class="anchor"
           id="user-content-类的-prototype-属性和__proto__属性">

    </a>类的 prototype 属性和__proto__属性
    </h2>
    <p>大多数浏览器的 ES5 实现之中，每一个对象都有<code>__proto__</code>属性，指向对应的构造函数的<code>prototype</code>属性。Class 作为构造函数的语法糖，同时有<code>prototype</code>属性和<code>__proto__</code>属性，因此同时存在两条继承链。
    </p>
    <p>（1）子类的<code>__proto__</code>属性，表示构造函数的继承，总是指向父类。</p>
    <p>（2）子类<code>prototype</code>属性的<code>__proto__</code>属性，表示方法的继承，总是指向父类的<code>prototype</code>属性。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
}

<span class="pl-k">class</span> <span class="pl-en">B</span> <span class="pl-k">extends</span> <span
                class="pl-e">A</span> {
}

<span class="pl-c1">B</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">===</span> <span
                class="pl-c1">A</span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">B</span>.<span class="pl-c1">prototype</span>.<span class="pl-smi">__proto__</span> <span
                class="pl-k">===</span> <span class="pl-c1">A</span>.<span class="pl-c1">prototype</span> <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>
        上面代码中，子类<code>B</code>的<code>__proto__</code>属性指向父类<code>A</code>，子类<code>B</code>的<code>prototype</code>属性的<code>__proto__</code>属性指向父类<code>A</code>的<code>prototype</code>属性。
    </p>
    <p>这样的结果是因为，类的继承是按照下面的模式实现的。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
}

<span class="pl-k">class</span> <span class="pl-en">B</span> {
}

<span class="pl-c"><span class="pl-c">//</span> B 的实例继承 A 的实例</span>
<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-c1">B</span>.<span
                class="pl-c1">prototype</span>, <span class="pl-c1">A</span>.<span class="pl-c1">prototype</span>);

<span class="pl-c"><span class="pl-c">//</span> B 的实例继承 A 的静态属性</span>
<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-c1">B</span>, <span
                class="pl-c1">A</span>);

<span class="pl-k">const</span> <span class="pl-c1">b</span> <span class="pl-k">=</span> <span
                class="pl-k">new</span> <span class="pl-en">B</span>();</pre>
    </div>
    <p>《对象的扩展》一章给出过<code>Object.setPrototypeOf</code>方法的实现。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span
            class="pl-en">setPrototypeOf</span> <span class="pl-k">=</span> <span class="pl-k">function</span> (<span
            class="pl-smi">obj</span>, <span class="pl-smi">proto</span>) {
  <span class="pl-smi">obj</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">=</span> proto;
  <span class="pl-k">return</span> obj;
}</pre>
    </div>
    <p>因此，就得到了上面的结果。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span
            class="pl-en">setPrototypeOf</span>(<span class="pl-c1">B</span>.<span class="pl-c1">prototype</span>, <span
            class="pl-c1">A</span>.<span class="pl-c1">prototype</span>);
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-c1">B</span>.<span class="pl-c1">prototype</span>.<span class="pl-smi">__proto__</span> <span
                class="pl-k">=</span> <span class="pl-c1">A</span>.<span class="pl-c1">prototype</span>;

<span class="pl-c1">Object</span>.<span class="pl-en">setPrototypeOf</span>(<span class="pl-c1">B</span>, <span
                class="pl-c1">A</span>);
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-c1">B</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">=</span> <span
                class="pl-c1">A</span>;</pre>
    </div>
    <p>
        这两条继承链，可以这样理解：作为一个对象，子类（<code>B</code>）的原型（<code>__proto__</code>属性）是父类（<code>A</code>）；作为一个构造函数，子类（<code>B</code>）的原型对象（<code>prototype</code>属性）是父类的原型对象（<code>prototype</code>属性）的实例。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-c1">Object</span>.<span class="pl-en">create</span>(<span
            class="pl-c1">A</span>.<span class="pl-c1">prototype</span>);
<span class="pl-c"><span class="pl-c">//</span> 等同于</span>
<span class="pl-c1">B</span>.<span class="pl-c1">prototype</span>.<span class="pl-smi">__proto__</span> <span
                class="pl-k">=</span> <span class="pl-c1">A</span>.<span class="pl-c1">prototype</span>;</pre>
    </div>
    <h3><a href="#extends-的继承目标"  class="anchor" id="user-content-extends-的继承目标">

    </a>extends 的继承目标
    </h3>
    <p><code>extends</code>关键字后面可以跟多种类型的值。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">B</span> <span
            class="pl-k">extends</span> <span class="pl-e">A</span> {
}</pre>
    </div>
    <p>上面代码的<code>A</code>，只要是一个有<code>prototype</code>属性的函数，就能被<code>B</code>继承。由于函数都有<code>prototype</code>属性（除了<code>Function.prototype</code>函数），因此<code>A</code>可以是任意函数。
    </p>
    <p>下面，讨论三种特殊情况。</p>
    <p>第一种特殊情况，子类继承<code>Object</code>类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> <span
            class="pl-k">extends</span> <span class="pl-e">Object</span> {
}

<span class="pl-c1">A</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">===</span> <span class="pl-c1">Object</span> <span
                class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">A</span>.<span class="pl-c1">prototype</span>.<span class="pl-smi">__proto__</span> <span
                class="pl-k">===</span> <span class="pl-c1">Object</span>.<span class="pl-c1">prototype</span> <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>这种情况下，<code>A</code>其实就是构造函数<code>Object</code>的复制，<code>A</code>的实例就是<code>Object</code>的实例。</p>
    <p>第二种特殊情况，不存在任何继承。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> {
}

<span class="pl-c1">A</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">===</span> <span class="pl-c1">Function</span>.<span
                class="pl-c1">prototype</span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">A</span>.<span class="pl-c1">prototype</span>.<span class="pl-smi">__proto__</span> <span
                class="pl-k">===</span> <span class="pl-c1">Object</span>.<span class="pl-c1">prototype</span> <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>这种情况下，<code>A</code>作为一个基类（即不存在任何继承），就是一个普通函数，所以直接继承<code>Function.prototype</code>。但是，<code>A</code>调用后返回一个空对象（即<code>Object</code>实例），所以<code>A.prototype.__proto__</code>指向构造函数（<code>Object</code>）的<code>prototype</code>属性。
    </p>
    <p>第三种特殊情况，子类继承<code>null</code>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">A</span> <span
            class="pl-k">extends</span> <span class="pl-e">null</span> {
}

<span class="pl-c1">A</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">===</span> <span class="pl-c1">Function</span>.<span
                class="pl-c1">prototype</span> <span class="pl-c"><span class="pl-c">//</span> true</span>
<span class="pl-c1">A</span>.<span class="pl-c1">prototype</span>.<span class="pl-smi">__proto__</span> <span
                class="pl-k">===</span> <span class="pl-c1">undefined</span> <span class="pl-c"><span
                class="pl-c">//</span> true</span></pre>
    </div>
    <p>
        这种情况与第二种情况非常像。<code>A</code>也是一个普通函数，所以直接继承<code>Function.prototype</code>。但是，<code>A</code>调用后返回的对象不继承任何方法，所以它的<code>__proto__</code>指向<code>Function.prototype</code>，即实质上执行了下面的代码。
    </p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span class="pl-en">C</span> <span
            class="pl-k">extends</span> <span class="pl-e">null</span> {
  <span class="pl-en">constructor</span>() { <span class="pl-k">return</span> <span class="pl-c1">Object</span>.<span
                class="pl-en">create</span>(<span class="pl-c1">null</span>); }
}</pre>
    </div>
    <h3><a href="#实例的-__proto__-属性"  class="anchor" id="user-content-实例的-__proto__-属性">

    </a>实例的 __proto__ 属性
    </h3>
    <p>子类实例的<code>__proto__</code>属性的<code>__proto__</code>属性，指向父类实例的<code>__proto__</code>属性。也就是说，子类的原型的原型，是父类的原型。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> p1 <span class="pl-k">=</span> <span
            class="pl-k">new</span> <span class="pl-en">Point</span>(<span class="pl-c1">2</span>, <span
            class="pl-c1">3</span>);
<span class="pl-k">var</span> p2 <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">ColorPoint</span>(<span
                class="pl-c1">2</span>, <span class="pl-c1">3</span>, <span class="pl-s"><span class="pl-pds">'</span>red<span
                class="pl-pds">'</span></span>);

<span class="pl-smi">p2</span>.<span class="pl-smi">__proto__</span> <span class="pl-k">===</span> <span class="pl-smi">p1</span>.<span
                class="pl-smi">__proto__</span> <span class="pl-c"><span class="pl-c">//</span> false</span>
<span class="pl-smi">p2</span>.<span class="pl-smi">__proto__</span>.<span class="pl-smi">__proto__</span> <span
                class="pl-k">===</span> <span class="pl-smi">p1</span>.<span class="pl-smi">__proto__</span> <span
                class="pl-c"><span class="pl-c">//</span> true</span></pre>
    </div>
    <p>上面代码中，<code>ColorPoint</code>继承了<code>Point</code>，导致前者原型的原型是后者的原型。</p>
    <p>因此，通过子类实例的<code>__proto__.__proto__</code>属性，可以修改父类实例的行为。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-smi">p2</span>.<span class="pl-smi">__proto__</span>.<span
            class="pl-smi">__proto__</span>.<span class="pl-en">printName</span> <span class="pl-k">=</span> <span
            class="pl-k">function</span> () {
  <span class="pl-en">console</span>.<span class="pl-c1">log</span>(<span class="pl-s"><span
                class="pl-pds">'</span>Ha<span class="pl-pds">'</span></span>);
};

<span class="pl-smi">p1</span>.<span class="pl-en">printName</span>() <span class="pl-c"><span class="pl-c">//</span> "Ha"</span></pre>
    </div>
    <p>
        上面代码在<code>ColorPoint</code>的实例<code>p2</code>上向<code>Point</code>类添加方法，结果影响到了<code>Point</code>的实例<code>p1</code>。
    </p>
    <h2><a href="#原生构造函数的继承"  class="anchor" id="user-content-原生构造函数的继承">

    </a>原生构造函数的继承
    </h2>
    <p>原生构造函数是指语言内置的构造函数，通常用来生成数据结构。ECMAScript 的原生构造函数大致有下面这些。</p>
    <ul>
        <li>Boolean()</li>
        <li>Number()</li>
        <li>String()</li>
        <li>Array()</li>
        <li>Date()</li>
        <li>Function()</li>
        <li>RegExp()</li>
        <li>Error()</li>
        <li>Object()</li>
    </ul>
    <p>以前，这些原生构造函数是无法继承的，比如，不能自己定义一个<code>Array</code>的子类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">MyArray</span>() {
  <span class="pl-c1">Array</span>.<span class="pl-c1">apply</span>(<span class="pl-c1">this</span>, <span
                class="pl-c1">arguments</span>);
}

<span class="pl-smi">MyArray</span>.<span class="pl-c1">prototype</span> <span class="pl-k">=</span> <span
                class="pl-c1">Object</span>.<span class="pl-en">create</span>(<span class="pl-c1">Array</span>.<span
                class="pl-c1">prototype</span>, {
  <span class="pl-en">constructor</span>: {
    value: MyArray,
    writable: true,
    configurable: true,
    enumerable: true
  }
});</pre>
    </div>
    <p>上面代码定义了一个继承 Array 的<code>MyArray</code>类。但是，这个类的行为与<code>Array</code>完全不一致。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> colors <span
            class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">MyArray</span>();
colors[<span class="pl-c1">0</span>] <span class="pl-k">=</span> <span class="pl-s"><span
                class="pl-pds">"</span>red<span class="pl-pds">"</span></span>;
<span class="pl-smi">colors</span>.<span class="pl-c1">length</span>  <span class="pl-c"><span class="pl-c">//</span> 0</span>

<span class="pl-smi">colors</span>.<span class="pl-c1">length</span> <span class="pl-k">=</span> <span
                class="pl-c1">0</span>;
colors[<span class="pl-c1">0</span>]  <span class="pl-c"><span class="pl-c">//</span> "red"</span></pre>
    </div>
    <p>之所以会发生这种情况，是因为子类无法获得原生构造函数的内部属性，通过<code>Array.apply()</code>或者分配给原型对象都不行。原生构造函数会忽略<code>apply</code>方法传入的<code>this</code>，也就是说，原生构造函数的<code>this</code>无法绑定，导致拿不到内部属性。
    </p>
    <p>ES5 是先新建子类的实例对象<code>this</code>，再将父类的属性添加到子类上，由于父类的内部属性无法获取，导致无法继承原生的构造函数。比如，<code>Array</code>构造函数有一个内部属性<code>[[DefineOwnProperty]]</code>，用来定义新属性时，更新<code>length</code>属性，这个内部属性无法在子类获取，导致子类的<code>length</code>属性行为不正常。
    </p>
    <p>下面的例子中，我们想让一个普通对象继承<code>Error</code>对象。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">var</span> e <span class="pl-k">=</span> {};

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyNames</span>(<span class="pl-c1">Error</span>.<span
                class="pl-c1">call</span>(e))
<span class="pl-c"><span class="pl-c">//</span> [ 'stack' ]</span>

<span class="pl-c1">Object</span>.<span class="pl-en">getOwnPropertyNames</span>(e)
<span class="pl-c"><span class="pl-c">//</span> []</span></pre>
    </div>
    <p>
        上面代码中，我们想通过<code>Error.call(e)</code>这种写法，让普通对象<code>e</code>具有<code>Error</code>对象的实例属性。但是，<code>Error.call()</code>完全忽略传入的第一个参数，而是返回一个新对象，<code>e</code>本身没有任何变化。这证明了<code>Error.call(e)</code>这种写法，无法继承原生构造函数。
    </p>
    <p>ES6 允许继承原生构造函数定义子类，因为 ES6
        是先新建父类的实例对象<code>this</code>，然后再用子类的构造函数修饰<code>this</code>，使得父类的所有行为都可以继承。下面是一个继承<code>Array</code>的例子。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">MyArray</span> <span class="pl-k">extends</span> <span class="pl-e">Array</span> {
  <span class="pl-en">constructor</span>(<span class="pl-k">...</span><span class="pl-v">args</span>) {
    <span class="pl-c1">super</span>(<span class="pl-k">...</span>args);
  }
}

<span class="pl-k">var</span> arr <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">MyArray</span>();
arr[<span class="pl-c1">0</span>] <span class="pl-k">=</span> <span class="pl-c1">12</span>;
<span class="pl-smi">arr</span>.<span class="pl-c1">length</span> <span class="pl-c"><span
                class="pl-c">//</span> 1</span>

<span class="pl-smi">arr</span>.<span class="pl-c1">length</span> <span class="pl-k">=</span> <span
                class="pl-c1">0</span>;
arr[<span class="pl-c1">0</span>] <span class="pl-c"><span class="pl-c">//</span> undefined</span></pre>
    </div>
    <p>上面代码定义了一个<code>MyArray</code>类，继承了<code>Array</code>构造函数，因此就可以从<code>MyArray</code>生成数组的实例。这意味着，ES6
        可以自定义原生数据结构（比如<code>Array</code>、<code>String</code>等）的子类，这是 ES5 无法做到的。</p>
    <p>上面这个例子也说明，<code>extends</code>关键字不仅可以用来继承类，还可以用来继承原生的构造函数。因此可以在原生数据结构的基础上，定义自己的数据结构。下面就是定义了一个带版本功能的数组。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">VersionedArray</span> <span class="pl-k">extends</span> <span class="pl-e">Array</span> {
  <span class="pl-en">constructor</span>() {
    <span class="pl-c1">super</span>();
    <span class="pl-c1">this</span>.<span class="pl-c1">history</span> <span class="pl-k">=</span> [[]];
  }
  <span class="pl-en">commit</span>() {
    <span class="pl-c1">this</span>.<span class="pl-c1">history</span>.<span class="pl-c1">push</span>(<span
                class="pl-c1">this</span>.<span class="pl-c1">slice</span>());
  }
  <span class="pl-en">revert</span>() {
    <span class="pl-c1">this</span>.<span class="pl-c1">splice</span>(<span class="pl-c1">0</span>, <span class="pl-c1">this</span>.<span
                class="pl-c1">length</span>, <span class="pl-k">...</span><span class="pl-c1">this</span>.<span
                class="pl-c1">history</span>[<span class="pl-c1">this</span>.<span class="pl-c1">history</span>.<span
                class="pl-c1">length</span> <span class="pl-k">-</span> <span class="pl-c1">1</span>]);
  }
}

<span class="pl-k">var</span> x <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">VersionedArray</span>();

<span class="pl-smi">x</span>.<span class="pl-c1">push</span>(<span class="pl-c1">1</span>);
<span class="pl-smi">x</span>.<span class="pl-c1">push</span>(<span class="pl-c1">2</span>);
x <span class="pl-c"><span class="pl-c">//</span> [1, 2]</span>
<span class="pl-smi">x</span>.<span class="pl-c1">history</span> <span class="pl-c"><span
                class="pl-c">//</span> [[]]</span>

<span class="pl-smi">x</span>.<span class="pl-en">commit</span>();
<span class="pl-smi">x</span>.<span class="pl-c1">history</span> <span class="pl-c"><span class="pl-c">//</span> [[], [1, 2]]</span>

<span class="pl-smi">x</span>.<span class="pl-c1">push</span>(<span class="pl-c1">3</span>);
x <span class="pl-c"><span class="pl-c">//</span> [1, 2, 3]</span>
<span class="pl-smi">x</span>.<span class="pl-c1">history</span> <span class="pl-c"><span class="pl-c">//</span> [[], [1, 2]]</span>

<span class="pl-smi">x</span>.<span class="pl-en">revert</span>();
x <span class="pl-c"><span class="pl-c">//</span> [1, 2]</span></pre>
    </div>
    <p>
        上面代码中，<code>VersionedArray</code>会通过<code>commit</code>方法，将自己的当前状态生成一个版本快照，存入<code>history</code>属性。<code>revert</code>方法用来将数组重置为最新一次保存的版本。除此之外，<code>VersionedArray</code>依然是一个普通数组，所有原生的数组方法都可以在它上面调用。
    </p>
    <p>下面是一个自定义<code>Error</code>子类的例子，可以用来定制报错时的行为。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">ExtendableError</span> <span class="pl-k">extends</span> <span class="pl-e">Error</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">message</span>) {
    <span class="pl-c1">super</span>();
    <span class="pl-c1">this</span>.<span class="pl-smi">message</span> <span class="pl-k">=</span> message;
    <span class="pl-c1">this</span>.<span class="pl-smi">stack</span> <span class="pl-k">=</span> (<span class="pl-k">new</span> <span
                class="pl-en">Error</span>()).<span class="pl-smi">stack</span>;
    <span class="pl-c1">this</span>.<span class="pl-c1">name</span> <span class="pl-k">=</span> <span
                class="pl-c1">this</span>.<span class="pl-c1">constructor</span>.<span class="pl-c1">name</span>;
  }
}

<span class="pl-k">class</span> <span class="pl-en">MyError</span> <span class="pl-k">extends</span> <span class="pl-e">ExtendableError</span> {
  <span class="pl-en">constructor</span>(<span class="pl-smi">m</span>) {
    <span class="pl-c1">super</span>(m);
  }
}

<span class="pl-k">var</span> myerror <span class="pl-k">=</span> <span class="pl-k">new</span> <span class="pl-en">MyError</span>(<span
                class="pl-s"><span class="pl-pds">'</span>ll<span class="pl-pds">'</span></span>);
<span class="pl-smi">myerror</span>.<span class="pl-smi">message</span> <span class="pl-c"><span class="pl-c">//</span> "ll"</span>
myerror <span class="pl-k">instanceof</span> <span class="pl-c1">Error</span> <span class="pl-c"><span
                class="pl-c">//</span> true</span>
<span class="pl-smi">myerror</span>.<span class="pl-c1">name</span> <span class="pl-c"><span class="pl-c">//</span> "MyError"</span>
<span class="pl-smi">myerror</span>.<span class="pl-smi">stack</span>
<span class="pl-c"><span class="pl-c">//</span> Error</span>
<span class="pl-c"><span class="pl-c">//</span>     at MyError.ExtendableError</span>
<span class="pl-c"><span class="pl-c">//</span>     ...</span></pre>
    </div>
    <p>注意，继承<code>Object</code>的子类，有一个<a
            href="http://stackoverflow.com/questions/36203614/super-does-not-pass-arguments-when-instantiating-a-class-extended-from-object"
            rel="nofollow">行为差异</a>。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">NewObj</span> <span class="pl-k">extends</span> <span class="pl-e">Object</span>{
  <span class="pl-en">constructor</span>(){
    <span class="pl-c1">super</span>(<span class="pl-k">...</span><span class="pl-c1">arguments</span>);
  }
}
<span class="pl-k">var</span> o <span class="pl-k">=</span> <span class="pl-k">new</span> <span
                class="pl-en">NewObj</span>({attr<span class="pl-k">:</span> <span class="pl-c1">true</span>});
<span class="pl-smi">o</span>.<span class="pl-smi">attr</span> <span class="pl-k">===</span> <span
                class="pl-c1">true</span>  <span class="pl-c"><span class="pl-c">//</span> false</span></pre>
    </div>
    <p>上面代码中，<code>NewObj</code>继承了<code>Object</code>，但是无法通过<code>super</code>方法向父类<code>Object</code>传参。这是因为 ES6
        改变了<code>Object</code>构造函数的行为，一旦发现<code>Object</code>方法不是通过<code>new Object()</code>这种形式调用，ES6
        规定<code>Object</code>构造函数会忽略参数。</p>
    <h2><a href="#mixin-模式的实现"  class="anchor" id="user-content-mixin-模式的实现">

    </a>Mixin 模式的实现
    </h2>
    <p>Mixin 指的是多个对象合成一个新的对象，新对象具有各个组成成员的接口。它的最简单实现如下。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">const</span> <span class="pl-c1">a</span> <span
            class="pl-k">=</span> {
  a<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>a<span class="pl-pds">'</span></span>
};
<span class="pl-k">const</span> <span class="pl-c1">b</span> <span class="pl-k">=</span> {
  b<span class="pl-k">:</span> <span class="pl-s"><span class="pl-pds">'</span>b<span class="pl-pds">'</span></span>
};
<span class="pl-k">const</span> <span class="pl-c1">c</span> <span class="pl-k">=</span> {<span class="pl-k">...</span>a, <span
                class="pl-k">...</span>b}; <span class="pl-c"><span class="pl-c">//</span> {a: 'a', b: 'b'}</span></pre>
    </div>
    <p>上面代码中，<code>c</code>对象是<code>a</code>对象和<code>b</code>对象的合成，具有两者的接口。</p>
    <p>下面是一个更完备的实现，将多个类的接口“混入”（mix in）另一个类。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">function</span> <span
            class="pl-en">mix</span>(<span class="pl-k">...</span><span class="pl-v">mixins</span>) {
  <span class="pl-k">class</span> <span class="pl-en">Mix</span> {}

  <span class="pl-k">for</span> (<span class="pl-k">let</span> mixin <span class="pl-k">of</span> mixins) {
    <span class="pl-en">copyProperties</span>(Mix, mixin); <span class="pl-c"><span class="pl-c">//</span> 拷贝实例属性</span>
    <span class="pl-en">copyProperties</span>(<span class="pl-smi">Mix</span>.<span
                class="pl-c1">prototype</span>, <span class="pl-smi">mixin</span>.<span class="pl-c1">prototype</span>); <span
                class="pl-c"><span class="pl-c">//</span> 拷贝原型属性</span>
  }

  <span class="pl-k">return</span> Mix;
}

<span class="pl-k">function</span> <span class="pl-en">copyProperties</span>(<span class="pl-smi">target</span>, <span
                class="pl-smi">source</span>) {
  <span class="pl-k">for</span> (<span class="pl-k">let</span> key <span class="pl-k">of</span> <span class="pl-c1">Reflect</span>.<span
                class="pl-en">ownKeys</span>(source)) {
    <span class="pl-k">if</span> ( key <span class="pl-k">!==</span> <span class="pl-s"><span class="pl-pds">"</span>constructor<span
                class="pl-pds">"</span></span>
      <span class="pl-k">&amp;&amp;</span> key <span class="pl-k">!==</span> <span class="pl-s"><span
                class="pl-pds">"</span>prototype<span class="pl-pds">"</span></span>
      <span class="pl-k">&amp;&amp;</span> key <span class="pl-k">!==</span> <span class="pl-s"><span
                class="pl-pds">"</span>name<span class="pl-pds">"</span></span>
    ) {
      <span class="pl-k">let</span> desc <span class="pl-k">=</span> <span class="pl-c1">Object</span>.<span
                class="pl-en">getOwnPropertyDescriptor</span>(source, key);
      <span class="pl-c1">Object</span>.<span class="pl-en">defineProperty</span>(target, key, desc);
    }
  }
}</pre>
    </div>
    <p>上面代码的<code>mix</code>函数，可以将多个对象合成为一个类。使用的时候，只要继承这个类即可。</p>
    <div class="highlight highlight-source-js"><pre><span class="pl-k">class</span> <span
            class="pl-en">DistributedEdit</span> <span class="pl-k">extends</span> <span class="pl-e">mix</span>(Loggable, Serializable) {
  <span class="pl-c"><span class="pl-c">//</span> ...</span>
}</pre>
    </div>
</div>
</body>
</html>
